﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using ReactiveLang;

namespace TimeZoneConverter
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        TimeZoneInfo localTimeZone;
        ReactiveHost _reactive = new ReactiveHost();

        public MainWindow()
        {
            InitializeComponent();

            srcTime.Text = DateTime.Now.ToLongTimeString();

            foreach (var tz in TimeZoneInfo.GetSystemTimeZones())
            {
                targetTimeZone.Items.Add(tz);
                if (tz.DisplayName.Contains("Tashkent"))
                    targetTimeZone.SelectedItem = tz;

                srcTimeZone.Items.Add(tz);
                if (tz.BaseUtcOffset == TimeZoneInfo.Local.BaseUtcOffset)
                    srcTimeZone.SelectedItem = localTimeZone = tz;
            }

            // create the rule set 
            // and save a reference to it to prevent it to be collected by the GC
            _reactive.Add(MyReactiveMethod());
        }

        struct DateTimeTryParseResult
        {
            public bool ReturnValue;
            public DateTime Value;

            public override string ToString()
            {
                return "{ ReturnValue = " + ReturnValue + ", Value = " + Value + " }";
            }
        }
        static DateTimeTryParseResult DateTimeTryParse(string text)
        {
            DateTimeTryParseResult result;
            result.ReturnValue = DateTime.TryParse(text, out result.Value);
            return result;
        }

        Func<IExpression<Unit>> _myReactiveMethod;
        IExpression<Unit> MyReactiveMethod()
        {
            if (_myReactiveMethod == null)
            {
                _myReactiveMethod = delegate
                {
                    // variables of the live method
                    // the same as in the TimeZoneConverter.LiveCode plus a couple of additional ones
                    var sourceTime = new Variable<DateTime>();
                    var tryParseResult = new Variable<DateTimeTryParseResult>();
                    var sourceTimeZone = new Variable<TimeZoneInfo>();
                    var destinationTimeZone = new Variable<TimeZoneInfo>();

                    var f = new Factory<Unit>();

                    return f.Call
                    (
                        // if (useCurrentTime.IsChecked == true)
                        f.If(() => useCurrentTime.IsChecked == true,

                            f.Block(
                                f.Assign(srcTime, Control.IsEnabledProperty, false),
                                f.Assign(srcTimeZone, Control.IsEnabledProperty, false),
                                f.Assign(srcTimeZone, ComboBox.SelectedItemProperty, localTimeZone),
                                f.Assign(sourceTime, () => DateTime.SpecifyKind(DateTime.Now, DateTimeKind.Unspecified)),
                                f.Assign(srcTime, TextBox.TextProperty, () => sourceTime.Value.ToLongTimeString())
                            ),

                            f.Block(
                                f.Assign(tryParseResult, () => DateTimeTryParse(srcTime.Text)),

                                 // if (!tryParseResult.Result)
                                f.If(() => !tryParseResult.Value.ReturnValue,
                                    f.Block(
                                        f.Assign(srcTime, Control.BackgroundProperty, Brushes.LightPink),
                                        f.Return(f.Unit)
                                    ),

                                    f.Assign(sourceTime, () => tryParseResult.Value.Value)
                                )
                            )
                        ),

                        f.Assign(sourceTimeZone, () => (TimeZoneInfo) srcTimeZone.SelectedItem),
                        f.Assign(destinationTimeZone, () => (TimeZoneInfo) targetTimeZone.SelectedItem),

                        f.If(() => sourceTimeZone != null && destinationTimeZone != null,
                            f.Imperative(
                                () => TimeZoneInfo.ConvertTime(sourceTime.Value, sourceTimeZone.Value, destinationTimeZone.Value).ToLongTimeString(), 
                                x => targetTime.Text = x
                            )
                        )
                    );
                };
            }
            return _myReactiveMethod();
        }
    }
}
